/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Model;
import java.awt.Color;
import java.util.*;

/**
 *
 * @author snowangelic
 */
public class Kmeans {
    private String name;
    private DataSet dataSet;
    private Distance distance;
    private ArrayList<Cluster> clusterList;
    private ArrayList<Weight> weightList;

    Kmeans(String name,Distance distance,DataSet dataSet){
        this.name=name;
        this.distance=distance;
        this.clusterList=new ArrayList<Cluster>();
        this.weightList=new ArrayList<Weight>();
        this.dataSet=dataSet;
    }

    public void addCluster(String name,Color color){
        Cluster cluster=new Cluster(this,name,color);
        clusterList.add(cluster);
    }

    public void addCluster(String name,String color){
        Cluster cluster=new Cluster(this,name,color);
        clusterList.add(cluster);
    }

    public void AddWeight(Attribute attribute,float weightVal){
        Weight weight=new Weight(attribute,weightVal);
        weightList.add(weight);
    }
    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the distance
     */
    public Distance getDistance() {
        return distance;
    }

    /**
     * @return the clusterList
     */
    public ArrayList<Cluster> getClusterList() {
        return clusterList;
    }

    /**
     * @return the weightList
     */
    public ArrayList<Weight> getWeightList() {
        return weightList;
    }

    // --- Kmeans Algorithm ---

    /**
     *
     */
    public void compute(){
        // --- 1: Normaliser les données ---

        /* Pour chaque paramètre, on recherche
         * le facteur de normalisation */

        Iterator it=weightList.iterator();
        while(it.hasNext()){
           Weight attrWeight=(Weight) it.next();
           float normPar=computeNormalizeParameter(attrWeight.getAttribute());
           attrWeight.setNormalizeParameter(normPar);
        }

        // --- 2: Initialiser les centroides ---
        ArrayList<Centroid> centroidList = new ArrayList<Centroid>();

        Iterator it2=clusterList.iterator();
        while(it2.hasNext()){
           Cluster cluster=(Cluster) it2.next();
           Centroid c=new Centroid(cluster); //WeightList utile pour éviter d'initialiser les attributs non utilisés lors du Kmeans
           centroidList.add(c);
        }

            // Tant que les centroides n'ont pas leur place définitive
        int[] currentState = new int[dataSet.getActionNbr()]; currentState[0]=1;
        int[] previousState = new int[dataSet.getActionNbr()]; previousState[0]=2;
        while(stateChanged(currentState,previousState)){
            previousState=currentState;
        // --- 3: Associer chaque point a son centroide le plus proche ---
            for(int i=0;i<dataSet.getActionNbr();i++){
                float minDist=Float.MAX_VALUE; //plus grande valeur possible pour un float
                Centroid nearestCentroid = null;

                Iterator it3=centroidList.iterator();
                while(it3.hasNext()){
                    Centroid currentCentroid=(Centroid) it3.next();
                    if(currentCentroid.getDistanceFromAction(dataSet.getAction(i) ,distance)<minDist){
                        minDist=currentCentroid.getDistanceFromAction(dataSet.getAction(i) ,distance);
                        nearestCentroid=currentCentroid;
                    }
                }
                dataSet.setCluster(i,nearestCentroid.getAssociatedCluster());
             }


        // --- 4: Reculculer la position des centroides ---
            Iterator it4=centroidList.iterator();
            while(it4.hasNext()){
                Centroid currentCentroid=(Centroid) it4.next();
                currentCentroid.recomputePosition(dataSet);
            }
            currentState=currentState();
        }
    }

    DataSet getDataSet() {
        return dataSet;
    }

    private float computeNormalizeParameter(Attribute attribute) {
        if(dataSet.getAttributeType(attribute)==AttributeType.TextualDiscreet){
            return 1;
        }
        else{
            float minVal=Float.MAX_VALUE;
            float maxVal=Float.NEGATIVE_INFINITY;
            for(int i=0;i<dataSet.getActionNbr();i++){
                if((Float)dataSet.getValue(i, attribute)<minVal) minVal=(Float)dataSet.getValue(i, attribute);
                if((Float)dataSet.getValue(i, attribute)>maxVal) maxVal=(Float)dataSet.getValue(i, attribute);
            }
            float ecart=maxVal-minVal;
            return ecart/100;
        }
    }

    private int[] currentState() {
        int[] clusterIndex = new int[dataSet.getActionNbr()];
        for(int i=0;i<dataSet.getActionNbr();i++){
            clusterIndex[i]=clusterList.indexOf(dataSet.getCluster(i,this));
        }
        return clusterIndex;
    }

    private boolean stateChanged(int[] currentState, int[] previousState) {
        if(currentState.length!=previousState.length) return true;
        else{
            for(int i=0;i<currentState.length;i++){
                if(currentState[i]!=previousState[i]) {
                    return true;
                }
            }
            return false;
        }
    }
    

}
